# 百度平台接口sdk
import base64

from ratelimit import limits, sleep_and_retry
from aip import AipFace
from aip import AipBodyAnalysis

from app.model import PhotoStandardModel
from app.dependencies import singleton
from app.model.baidu import BaiduFaceDetect



class DictToClass:
    def __init__(self, dictionary):
        for key, value in dictionary.items():
            if isinstance(value, dict):
                # 如果值是字典，递归转换为类的实例
                setattr(self, key, DictToClass(value))
            elif isinstance(value, list):
                # 如果值是列表，检查列表中的元素
                setattr(self, key, [DictToClass(item) if isinstance(item, dict) else item for item in value])
            else:
                setattr(self, key, value)


# 百度接口实例
@singleton
class BaiduClient:

    def __init__(self):
        # 鉴权参数后面考虑放入配置
        self.__appId = "116256768"
        self.__apiKey = "H6iWPyTxTcp9P953FLpUZ5p5"
        self.__secretKey = "Mtplsrg4jAGiI1rdaSTL7VW1y6lqHjyA"
        # 实例化
        self.__client_face = AipFace(self.__appId, self.__apiKey, self.__secretKey)
        self.__client_body = AipBodyAnalysis(self.__appId, self.__apiKey, self.__secretKey)

    # 人像标准照片参数
    def get_photo_standard_param(self, bs64 : str) -> PhotoStandardModel:
        result = PhotoStandardModel()
        result.options = self.__face_landmark150(bs64)
        # result.image_base64 = self.__body_seg(bs64)
        return result

    # 照片标注参数
    def get_photo_mark_param(self, bs64 : str) -> PhotoStandardModel:
        result = PhotoStandardModel()
        result.options = self.__face_landmark201(bs64)
        return result


    # 人脸关键点150
    @sleep_and_retry            # 等待放行
    @limits(calls=9, period=1) # 最多每秒10次
    def __face_landmark150(self, bs64 : str) -> BaiduFaceDetect:
        image_type = "BASE64"
        options = {
            "face_field": "age,expression,face_shape,gender,glasses,landmark150,quality,eye_status,emotion,face_type,mask,spoofing",    # 检测内容
            "max_face_num": 1,          # 检测人脸数量
            "face_type": "LIVE",        # 照片类型默认
            "liveness_control": "NONE",  # 活体质量控制
            "face_sort_type": 1,  # 人脸检测排序类型:1:代表检测出的人脸按照距离图片中心从近到远排列
        }
        resp = self.__client_face.detect(bs64, image_type, options)
        if "error_code" in resp:
          error_code = resp.get("error_code")
          if error_code != 0 :
            error_msg = resp.get("error_msg")
            raise Exception(f"__face_landmark [{error_code}]{error_msg}")
        if "result" not in resp:
            raise Exception("face landmark result is empty")
        if "face_list" not in resp.get("result") :  # 人脸结果列表判空
            raise Exception("face landmark face_list is empty")

        # 转换dict为实体
        resp_bean = DictToClass(resp)
        if len(resp_bean.result.face_list) > 1:  # 人脸结果列表限制1个
            raise Exception("face landmark face_list is exceed 1")

        ret = resp["result"]
        # dict_to_entity(BaiduFaceDetect, ret["face_list"][0])
        data = BaiduFaceDetect(**(ret["face_list"][0]))
        # data = resp_bean.result.face_list[0]
        # result.face_location = Rectangle(data.location.left, data.location.top, data.location.width, data.location.height)

        # landmark = data.landmark150

        # result.chin_position = Point(x=landmark.chin_2.x, y=landmark.chin_2.y)
        # result.cheek_left = Point(x=landmark.cheek_left_4.x, y=landmark.cheek_left_4.y)
        # result.cheek_right = Point(x=landmark.cheek_right_4.x, y=landmark.cheek_right_4.y)
        # result.nose_tip = Point(x=landmark.nose_tip.x, y=landmark.nose_tip.y)
        # result.rotation = data.location.rotation
        return data


    # 人脸关键点201
    @sleep_and_retry            # 等待放行
    @limits(calls=10, period=1) # 最多每秒10次
    def __face_landmark201(self, bs64 : str) -> BaiduFaceDetect:
        image_type = "BASE64"
        options = {
            "face_field": "landmark201",    # 检测内容
            "max_face_num": 1,          # 检测人脸数量
            "face_type": "LIVE",        # 照片类型默认
            "liveness_control": "NONE",  # 活体质量控制
            "face_sort_type": 1,  # 人脸检测排序类型:1:代表检测出的人脸按照距离图片中心从近到远排列
        }
        resp = self.__client_face.faceLandmarkV1(bs64, image_type, options)
        if "error_code" in resp:
          error_code = resp.get("error_code")
          if error_code != 0 :
            error_msg = resp.get("error_msg")
            raise Exception(f"__face_landmark [{error_code}]{error_msg}")
        if "result" not in resp:
            raise Exception("face landmark result is empty")
        if "face_list" not in resp.get("result") :  # 人脸结果列表判空
            raise Exception("face landmark face_list is empty")

        # 转换dict为实体
        resp_bean = DictToClass(resp)
        if len(resp_bean.result.face_list) > 1:  # 人脸结果列表限制1个
            raise Exception("face landmark face_list is exceed 1")

        ret = resp["result"]
        data = BaiduFaceDetect(**(ret["face_list"][0]))
        return data


    # 人像分割
    @sleep_and_retry            # 等待放行
    @limits(calls=10, period=1) # 最多每秒10次
    def __body_seg(self, bs64 : str) -> str:
        image_type = "BASE64"
        options = {
            # "type": "labelmap,foreground"  # 抠图类型：labelmap-二值图像;scoremap-人像灰度图;foreground-人像抠图
            "type": "foreground"
        }

        img_bin = base64.b64decode(bs64.split(';base64,')[-1])
        resp = self.__client_body.bodySeg(img_bin, options)
        if "error_code" in resp:
          error_code = resp.get("error_code")
          if error_code != 0 :
            error_msg = resp.get("error_msg")
            raise Exception(f"__body_seg [{error_code}]{error_msg}")
        if resp.get("person_num") > 1: # 检测到的人体框数目大于1
            raise Exception("person body num is exceed 1")
        if "foreground" not in resp :  # 人像前景抠图判空
            raise Exception("face landmark face_list is empty")

        return resp.get("foreground")
